

#include "nativeTypes.h"
#include "common.h"

#include <boost/shared_ptr.hpp>
#include <boost/assign/list_of.hpp>

#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/remove.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/logical/not.hpp>

using namespace awa;
using namespace boost::assign;

#define INDEX(z, n, param)		       param[n]
  //  (*static_cast<PUSH*>(param[n]))()
#define ARGUMENTS(n, param)			\
  this BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, INDEX, param.args)
#define CALLSTATEMENT(z, n, param)		\
  case n : \
  return (*(static_cast< NEWTYPE(n)* >(param.fn)))(ARGUMENTS(n, param)); \
  break;

#define CALLFN( param )						\
  switch( param.args.size() ) {					\
    BOOST_PP_REPEAT(2, CALLSTATEMENT, fnImpl)			\
  default :							\
      LOG(CRITICAL, "MAX ARGUMENTS REACHED !!!");		\
    assert(0);							\
  }


template<class T>
struct AwaInterface : public AwaAbstract {

  Generic EXEC( const Function& fnImpl ) {

    CALLFN( fnImpl );
  
  }

};


template< class T >
struct NativeTypeFnImpl : AwaInterface<T> {
  T var;
  

  Generic operator >  (const Generic& a) {
    return Generic( var > castTo<T>(a) );
  }

  Generic operator >= (const Generic& a) {
    return Generic( var >= castTo<T>(a) );
  }

  Generic operator < (const Generic& a) {
    return Generic( var < castTo<T>(a) );
  }
  Generic operator <= (const Generic& a) {
    return Generic( var <= castTo<T>(a) );
  }

  Generic operator == ( const Generic& a) {
    return Generic( var == castTo<T>(a) );
  }

  Generic operator != (const Generic& a) {
    return Generic( var != castTo<T>(a) );
  }

  Generic operator + (const Generic& a) {
    return Generic( var + castTo<T>(a) );
  }

  Generic operator - (const Generic& a) {
    return Generic( var - castTo<T>(a) );
  }

  Generic operator * (const Generic& a) {
    return Generic( var * castTo<T>(a) );
  }

  Generic operator / (const Generic& a) {
    return Generic( var / castTo<T>(a) );
  }

  Generic operator % (const Generic& a) {
    return Generic( T((int)var % castTo<int>(a)) );
  }

  Generic operator ^ (const Generic& a) {
    return Generic( T((int)var ^ castTo<int>(a)) );
  }

  Generic operator & (const Generic& a) {
    return Generic( T((int)var & castTo<int>(a)) );
  }

  Generic operator | (const Generic& a) {
    return Generic( T((int)var | castTo<int>(a)) );
  }
  
  Generic operator ~ () {
    return Generic( ~(int)var );
  }
  
  Generic operator ! () {
      return Generic( !var );
  }

  Generic operator && (const Generic& a) {
    return Generic( var && castTo<T>(a) );
  }

  Generic operator || (const Generic& a) {
    return Generic( var || castTo<T>(a) );
  }

  Generic inc () {
    return Generic( var + 1 );
  }
  
    Generic dec() {
    return Generic( var - 1);
  }
};

// template<class T>
// Generic UnMarshall(const InputSrc& token) {

//   std::stringstream ss;
//   ss << token;
//   typename T var;
//   ss >> var;
//   return Generic( var );

// };

// template<class T>
// InputSrc Marshall(const Genric& var) {

//   typename T a(typename castTo<typename T>(var));
//   std::stringstream ss;
//   ss << a;
//   return ss.str();

// };

template<class Element>
struct Array : virtual AwaInterface<Element*> {
  
  typedef boost::shared_ptr<Element>     SmartArray;

  unsigned long n;
  SmartArray var;
  
  Generic deref() {
    
    if( var )
      return Generic( *var );
    return Generic();
    
  }
  
  Generic deref_index(const Generic& x) {
    
    if( var )
      if(castTo<unsigned long>(x) >= n)
	return Generic();
      else
	return Generic( *(var.get() + castTo<unsigned long>(x)) );
    
    return Generic();
  }
  
  Generic ref() {
    
    if( var )
      return Generic( var.get() );
    return Generic();
    
  }
  
  Generic ref_index(const Generic& x) {

    if( var )
      if(castTo<unsigned long>(x) >= n)
	return Generic();
      else
      return Generic( var.get() + castTo<unsigned long>(x) );
    
    return Generic();
    
  }
  
  Generic allocate(const Generic& x) {
    unsigned long sz( castTo<unsigned long>(x) );
    if( !var )
      {
	n = sz;
	var = SmartArray(new Element[n]);
	return Generic( var.get() );
      }
    else // new copy
      {
	SmartArray tmp(new Element[sz]);
	std::copy(var.get(), var.get() + sz*sizeof(Element), tmp.get());
	return Generic(tmp.get());
      }
  }
};

template<>
struct Array<void> : virtual AwaInterface<void*> {

  typedef boost::shared_ptr<void>     SmartArray;

  unsigned long n;
  SmartArray var;
  
  Generic deref() {
    // TODO : Throw something
  }
  
  Generic deref_index(const Generic& x) {
    // TODO : Throw something
  }
  
  Generic ref() {
    
    if( var )
      return Generic( var.get() );
    return Generic();
    
  }
  
  Generic ref_index(const Generic& x) {
    //TODO: Throw something
  }
  
  Generic allocate(const Generic& x) {
    // TODO: Throw something
  }
};

typedef ImplCollection::value_type    Implementation;
typedef ObjectCollection::value_type            Object;
typedef TypeCollection::value_type              Type;

template<typename F, typename G> Generic function_assign(const Generic& g)
{  return g; }
template<class T>
ObjectCollection::value_type FunctionImpl(const char* name) {
  typedef boost::function1<Generic, T> FnPtr;

  Signature x;

  ObjectCollection::value_type fnMapping (name, map_list_of		\
					  (x = list_of("eq")(name), Impl(new FnPtr(&function_assign<T, T> )))
					  // (x = list_of("Marshall.do")(name), Impl(new FnPtr(&boost::function<T, T>::Marshall)))
					  );
  return fnMapping;
}
  
template<class T>
ObjectCollection::value_type ObjectImpl(const char* name){
  
  typedef boost::function2<Generic, NativeTypeFnImpl<T>*, Generic> FnPtr1;
  typedef boost::function1<Generic, NativeTypeFnImpl<T>* >         FnPtr0;
  
  Signature x;

  ObjectCollection::value_type ofnMapping(name, map_list_of
					  (x = list_of("inv"),          Impl(new FnPtr0(&NativeTypeFnImpl<T>::operator ! )))
					  (x = list_of("inc"),          Impl(new FnPtr0(&NativeTypeFnImpl<T>::inc )))
					  (x = list_of("dec"),          Impl(new FnPtr0(&NativeTypeFnImpl<T>::dec )))
					  (x = list_of("com"),          Impl(new FnPtr0(&NativeTypeFnImpl<T>::operator ~ )))
					  
					  (x = list_of("gt")( name),           Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator > )))
					  (x = list_of("ge")(name),           Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator >= )))
					  (x = list_of("lt")(name),           Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator < )))
					  (x = list_of("le")(name),	     Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator <= )))
					  (x = list_of("iseq")(name),         Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator == )))
					  (x = list_of("isneq")( name),        Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator != )))
					  (x = list_of("+")(name),            Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator + )))
					  (x = list_of("-")(name),            Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator - )))
					  (x = list_of("*")(name),            Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator * )))
					  (x = list_of("/")(name),	     Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator / )))
					  (x = list_of("mod")(name),          Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator % )))
					  (x = list_of("xor")(name),          Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator ^ )))
					  (x = list_of("and")(name),          Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator & )))
					  (x = list_of("or")(name),	     Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator | )))
					  (x = list_of("with")(name),         Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator && )))
					  (x = list_of("either")(name),       Impl(new FnPtr1(&NativeTypeFnImpl<T>::operator || )))
					  (x = list_of("inc"),          Impl(new FnPtr0(&NativeTypeFnImpl<T>::inc )))
					  (x = list_of("dec"),          Impl(new FnPtr0(&NativeTypeFnImpl<T>::dec )))
					  
					  //(Signature, Impl) ADD HERE
					  );
  return ofnMapping;
}
    
template<class T>
ObjectCollection::value_type PointerImpl(const char* name){
  typedef boost::function2<Generic, Array<T>*, Generic> FnPtr1;
  typedef boost::function1<Generic, Array<T>* >         FnPtr0;
    
  Signature x;

  ObjectCollection::value_type ofnMapping(name, map_list_of
					  (x = list_of("dref"),
					   Impl(new FnPtr0(&Array<T>::deref)))
					  (x = list_of("dref")("index"),
					   Impl(new FnPtr1(&Array<T>::deref_index)))
					  (x = list_of("ref")("index"),
					   Impl(new FnPtr1(&Array<T>::ref_index)))
					  (x = list_of("ref"),
					   Impl(new FnPtr0(&Array<T>::ref)))
					  (x = list_of("alloc")("sz"),
					   Impl(new FnPtr1(&Array<T>::allocate)))
					  );
}

    // { Type("type", 
    //              Object({"object", 
    //                                (Implementation*)({ Signature({"object"}), Impl })   
    //                    }) End Object
    //       )  End Type
    // }     End list of Types


#define OBJECTIMPL(SYMBOL, TYPEMAP)					\
  (ObjectImpl<TYPEMAP>(BOOST_PP_STRINGIZE(SYMBOL)).first, ObjectImpl<TYPEMAP>(BOOST_PP_STRINGIZE(SYMBOL)).second) \
  (PointerImpl<TYPEMAP>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(_, SYMBOL))).first, PointerImpl<TYPEMAP>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(_, SYMBOL))).second) \
  (PointerImpl<TYPEMAP*>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(__, SYMBOL))).first, PointerImpl<TYPEMAP*>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(__, SYMBOL))).second)

#define FUNCTIONIMPL(TYPE) \
  (FunctionImpl<TYPE>(BOOST_PP_STRINGIZE(TYPE)).first, FunctionImpl<TYPE>(BOOST_PP_STRINGIZE(TYPE)).second)			\
  (PointerImpl<TYPE>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(_, TYPE))).first, PointerImpl<TYPE>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(_, TYPE))).second) \
  (PointerImpl<TYPE*>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(__, TYPE))).first, PointerImpl<TYPE*>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(__, TYPE))).second)

#define NOVOID           BOOST_PP_SEQ_REMOVE(ELIGIBLE_TYPES, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(ELIGIBLE_TYPES)))
#define ISPRIMITIF(element) BOOST_PP_SEQ_SIZE(TYPE(element))
  

#define SYMBOL(element) \
  (BOOST_PP_SEQ_ELEM(1, element))
#define TYPE(element)			\
  BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_ELEM(2, element))

#define OBIMPL(r, data, element)	       \
  BOOST_PP_EXPR_IF( ISPRIMITIF(element), \
		    OBJECTIMPL(BOOST_PP_IDENTITY SYMBOL(element)(), \
				  BOOST_PP_IDENTITY TYPE(element)()) \
		    )
#define FNIMPL(r, data, element)		\
  BOOST_PP_EXPR_IF(BOOST_PP_NOT(ISPRIMITIF(element)),\
		   FUNCTIONIMPL(BOOST_PP_IDENTITY SYMBOL(element)()))

#define NATIVEO					\
  BOOST_PP_SEQ_FOR_EACH(OBIMPL, ~, NOVOID)
#define NATIVEF					\
  BOOST_PP_SEQ_FOR_EACH(FNIMPL, ~, ELIGIBLE_TYPES)

#define FUNCTOR(n)				\
  BOOST_PP_CAT(boost::function, n)<Generic BOOST_PP_COMMA_IF(n) ENUM(n, Generic)>
#define FNTYPEDEF(z, n, data)			\
  typedef FUNCTOR(n) BOOST_PP_CAT(fn, n);
#define FNTYPE					\
  BOOST_PP_REPEAT(MAX_ARG, FNTYPEDEF, ~)

#define VoidPtrImpl				\
  (PointerImpl<void>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(_, void))).first, \
   PointerImpl<void>(BOOST_PP_STRINGIZE(BOOST_PP_CAT(_, void))).second) 

FNTYPE

ObjectCollection o;
ObjectCollection f;
TypeCollection NativeTypes =  map_list_of("OBJECT", o = map_list_of NATIVEO VoidPtrImpl)
		                         ("FUNCTION", o = map_list_of NATIVEF );


extern Generic Main(const Generic &);
std::pair<Signature, Impl> MainEntry =  std::pair<Signature, Impl>(list_of("Main"),  Impl(new boost::function1<Generic, const Generic&>(std::ptr_fun(&Main))));
